﻿using System;
using System.Collections.Generic;
using System.Drawing;
using CanvasControl.Interface;
using CanvasControl.Model;
using CanvasControl.View;

namespace CanvasControl.Behaviors
{
    /// <summary>
    /// Реализация поведения для отрисовки фона и сетки.
    /// </summary>
    public class BackgroundDrawingBehavior: ICanvasBehavior, IPainter
    {
        /// <summary>
        /// Шаг ячейки сетки. Если значения нет, то сетка не рисуется.
        /// </summary>
        readonly int? gridSize;

        /// <summary>
        /// Ширина рамки.
        /// </summary>
        readonly int borderWidth;

        /// <summary>
        /// Представление отрисовки фона.
        /// </summary>
        readonly IBackgroundView view;

        /// <summary>
        /// Экземпляр модели.
        /// </summary>
        CanvasModel canvas;

        /// <summary>
        /// Настройки отрисовки фона.
        /// </summary>
        public Appearance Background { get; private set; }

        /// <summary>
        /// Настройки отрисовки сетки.
        /// </summary>
        public LineAppearance Grid { get; private set; }

        /// <summary>
        /// Создаёт новый экземпляр поведения с указанием ширины рамки и шагом сетки.
        /// </summary>
        /// <param name="borderWidth">Ширина рамки вокруг области отрисовки.</param>
        /// <param name="gridSize">Шаг отрисовки сетки.</param>
        public BackgroundDrawingBehavior(int borderWidth, int gridSize) : this(borderWidth)
        {
            this.gridSize = gridSize;
        }

        /// <summary>
        /// Создаёт новый экземпляр поведения с указанием ширины рамки.
        /// Сетка не отрисовывается.
        /// </summary>
        /// <param name="borderWidth">Ширина рамки.</param>
        public BackgroundDrawingBehavior(int borderWidth)
        {
            Background = new Appearance();
            Grid = new LineAppearance();
            Background.Border.Width = this.borderWidth = borderWidth;
            view = new BackgroundView(Background, Grid);
        }

        public void Install(ICanvasView view, CanvasModel canvas)
        {
            this.canvas = canvas;
            canvas.AddPainter(PaintStages.BACKGROUND, this);
        }

        public void Uninstall(CanvasModel canvas1)
        {
            canvas.RemovePainter(this);
        }

        void IPainter.Draw(DrawingContext dc)
        {
            var clientRect = new Rectangle(new Point(), canvas.ViewPort.Size);
            view.DrawBackground(dc, clientRect);
            view.DrawBorder(dc, new Rectangle(0, 0, clientRect.Width - borderWidth, clientRect.Height - borderWidth));
            if (!gridSize.HasValue)
                return;
            view.DrawGrid(dc, CalculateGridLines());
        }

        /// <summary>
        /// Рассчитывает линии сетки для представления.
        /// </summary>
        IEnumerable<Line> CalculateGridLines()
        {
            var viewPort = canvas.ViewPort;
            var gSize = gridSize.GetValueOrDefault();
            Func<int, int> Adjust = i => i - i%gSize;
            var viewTopLeft = viewPort.ClientToGlobal(new Point(borderWidth, borderWidth));
            var leftVertAxis = Adjust(viewTopLeft.X);
            var topHorzAxis = Adjust(viewTopLeft.Y);
            var viewBottomRight = viewPort.ClientToGlobal(new Point(viewPort.Size.Width - borderWidth*2, viewPort.Size.Height - borderWidth*2));
            var rightVertAxis = Adjust(viewBottomRight.X);
            var bottomHorzAxis = Adjust(viewBottomRight.Y);

            for (var x = leftVertAxis; x <= rightVertAxis; x += gSize)
                yield return new Line(viewPort.GlobalToClient(new Point(x, viewTopLeft.Y)), viewPort.GlobalToClient(new Point(x, viewBottomRight.Y)));
            for (var y = topHorzAxis; y <= bottomHorzAxis; y += gSize)
                yield return new Line(viewPort.GlobalToClient(new Point(viewTopLeft.X, y)), viewPort.GlobalToClient(new Point(viewBottomRight.X, y)));
        }
    }
}
